home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume15 / lwf < prev    next >
Encoding:
Internet Message Format  |  1988-05-24  |  47.8 KB

  1. Subject:  v15i008:  ASCII to PostScript filter
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Barry Brachman <grads.cs.ubc.ca!brachman>
  7. Posting-number: Volume 15, Issue 8
  8. Archive-name: lwf
  9.  
  10. [  Features include multi-column, portrait, and indented output.
  11.    Think of this as a "pr" for a PostScript machine.  I love the
  12.    spelling of the prolog file...  I repacked things.  --r$ ]
  13.  
  14. Enclosed is lwf, an ASCII to PostScript filter, as recently advertised
  15. in comp.lang.postscript/info-postscript.  The name stands for
  16. LaserWriter Filter.
  17.  
  18. This program
  19. Barry Brachman           | {ihnp4!alberta,uw-beaver,uunet}!
  20. Dept. of Computer Science|  ubc-vision!ubc-csgrads!brachman
  21. Univ. of British Columbia| brachman@grads.cs.ubc.cdn
  22. Vancouver, B.C. V6T 1W5  | brachman%ubc.csnet@csnet-relay.arpa
  23. (604) 228-4327           | brachman@ubc.csnet
  24.  
  25. #! /bin/sh
  26. # This is a shell archive.  Remove anything before this line, then unpack
  27. # it by saving it into a file and typing "sh file".  To overwrite existing
  28. # files, type "sh file -c".  You can also feed this as standard input via
  29. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  30. # will see the following message at the end:
  31. #        "End of shell archive."
  32. # Contents:  Makefile README lwf.c lwf.man prologue range.c
  33. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  34. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  35.   echo shar: Will not clobber existing file \"'Makefile'\"
  36. else
  37. echo shar: Extracting \"'Makefile'\" \(1002 characters\)
  38. sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  39. X
  40. CFLAGS = -O
  41. X
  42. X# Where to put lwf
  43. LWF = /usr/local/lwf
  44. X
  45. X# Where to put the man page
  46. MAN = /usr/man/manl/lwf.l
  47. X
  48. X# Where to put the PostScript prologue file
  49. PROLOGUE = /usr/local/lib/lwf.prologue
  50. X
  51. X# Environment options:
  52. X#
  53. X# -DPR=\"where pr is if not /bin/pr\"
  54. X# -DSYSV            For System V (assumes SYSV-style pr)
  55. X# -DREVERSE=0        Set to non-zero if page reversal is to be the default
  56. X# -DHOSTNAME=\"yourhostname\"    If you don't have the hostname() system call
  57. ENV = -DPROLOGUE=\"$(PROLOGUE)\" -DREVERSE=0
  58. X
  59. lwf: lwf.o range.o
  60. X    cc -s -o lwf lwf.o range.o -lm
  61. X
  62. lwf.o: lwf.c
  63. X    cc $(CFLAGS) -c $(ENV) lwf.c
  64. X
  65. range.o: range.c
  66. X    cc $(CFLAGS) -c range.c
  67. X
  68. X# Create the program to demonstrate and debug the range checking routine
  69. range: range.c
  70. X    cc $(CFLAGS) -DRANGE_DEBUG -o range range.c
  71. X
  72. install: lwf lwf.man
  73. X    cp prologue $(PROLOGUE)
  74. X    chmod 0644 $(PROLOGUE)
  75. X    cp lwf $(LWF)
  76. X    chmod 0755 $(LWF)
  77. X    ls -l $(LWF)
  78. X    cp lwf.man $(MAN)
  79. X    rm -f lwf lwf.o range.o
  80. X
  81. lint:
  82. X    lint -abchx $(ENV) lwf.c
  83. X
  84. clean:
  85. X    rm -f core lwf lwf.o range.o
  86. X
  87. END_OF_FILE
  88. if test 1002 -ne `wc -c <'Makefile'`; then
  89.     echo shar: \"'Makefile'\" unpacked with wrong size!
  90. fi
  91. # end of 'Makefile'
  92. fi
  93. if test -f 'README' -a "${1}" != "-c" ; then 
  94.   echo shar: Will not clobber existing file \"'README'\"
  95. else
  96. echo shar: Extracting \"'README'\" \(1619 characters\)
  97. sed "s/^X//" >'README' <<'END_OF_FILE'
  98. X
  99. Lwf converts ASCII text files to PostScript.
  100. It has been tested on the Apple LaserWriter/LaserWriter+ and the NEC
  101. Silentwriter LC-890.  Most of the processing is done on the host
  102. rather than on the printer.  The name "lwf", perhaps poorly chosen, stands
  103. for "LaserWriter Filter".
  104. X
  105. There may be a couple of minor Berkeley-isms in the program (see the
  106. routine setup()) and it is somewhat Unix dependent (eg., popen(), /bin/pr
  107. are expected).  The output conforms to the Adobe 2.0 file structuring
  108. conventions.  The program is set up to use the (fixed-width) Courier font.
  109. If you use another font you'll have to modify the mapping table in lwf.c.
  110. I can't recommend a proportional font for this purpose.
  111. X
  112. The distribution includes the following files:
  113. X
  114. README      This file
  115. Makefile
  116. lwf.c       LaserWriter filter to convert ASCII text to PostScript
  117. range.c     Routine to check if a number is in a given range
  118. lwf.man     Manual page for lwf
  119. prologue    Default PostScript support file
  120. X
  121. Please check the Makefile and the configurable #defines in lwf.c.
  122. Check if the man page needs to be revised for your environment.
  123. You can then proceed with 'make install'.
  124. X
  125. If you come across any bugs, including nonconformance to Adobe 2.0, or
  126. make any changes you'd like to share please send mail to me rather than
  127. posting to the net.
  128. X
  129. Enjoy.
  130. X
  131. X-----
  132. Barry Brachman           | {ihnp4!alberta,uw-beaver,uunet}!
  133. Dept. of Computer Science|  ubc-vision!ubc-csgrads!brachman
  134. Univ. of British Columbia| brachman@grads.cs.ubc.cdn
  135. Vancouver, B.C. V6T 1W5  | brachman%ubc.csnet@csnet-relay.arpa
  136. X(604) 228-4327           | brachman@ubc.csnet
  137. X
  138. END_OF_FILE
  139. if test 1619 -ne `wc -c <'README'`; then
  140.     echo shar: \"'README'\" unpacked with wrong size!
  141. fi
  142. # end of 'README'
  143. fi
  144. if test -f 'lwf.c' -a "${1}" != "-c" ; then 
  145.   echo shar: Will not clobber existing file \"'lwf.c'\"
  146. else
  147. echo shar: Extracting \"'lwf.c'\" \(26651 characters\)
  148. sed "s/^X//" >'lwf.c' <<'END_OF_FILE'
  149. X
  150. X/* vi: set tabstop=4 : */
  151. X
  152. X/*
  153. X * lwf - Convert ASCII text to PostScript
  154. X *
  155. X * Usage:
  156. X *    lwf [-d] [-i#] [-l] [-m] [-olist] [-p[str]] [-P filename] [-s#] [-t#]
  157. X *                [-v] [file ...]
  158. X *
  159. X * Options:
  160. X *    -d            Debug mode
  161. X *    -i#            Indent each line # inches (so much for metric)
  162. X *    -l            Landscape instead of Portrait
  163. X *    -m            Use 3 hole punch margins
  164. X *    -olist        Only print pages in the specified range
  165. X *    -p[str]        Use pr to print, passing optional string
  166. X *    -P filename Copy prologue from filename instead of default
  167. X *    -r            Toggle page reversal flag (see Makefile)
  168. X *    -s#            Use point size #
  169. X *    -t#            Spaces between tab stops is # characters
  170. X *    -v            Verbose
  171. X *    -S            Standalone mode (print header page, use EOF's)
  172. X *
  173. X * If no files are specified, stdin is used.
  174. X * Form feeds handled
  175. X * Backspacing with underlining (or overprinting) works
  176. X * The output conforms to Adobe 2.0
  177. X *
  178. X * Problems:
  179. X *    - assumes fixed-width (non-proportional) font in some places
  180. X *    - can't back up (using backspaces) over tabs
  181. X *    - assumes 8.5 x 11.0 paper
  182. X *
  183. X * BJB - Jun/87
  184. X * ========================================================================
  185. X *
  186. X * Permission is given to freely copy and distribute this software provided:
  187. X *
  188. X *    1) You do not sell it,
  189. X *    2) You do not use it for commercial advantage, and
  190. X *    3) This notice accompanies the distribution
  191. X *
  192. X * Copyright (c) 1988
  193. X * Barry Brachman
  194. X * Dept. of Computer Science
  195. X * Univ. of British Columbia
  196. X * Vancouver, B.C. V6T 1W5
  197. X *
  198. X * .. {ihnp4!alberta, uw-beaver, uunet}!ubc-vision!ubc-csgrads!brachman
  199. X * brachman@grads.cs.ubc.cdn
  200. X * brachman%ubc.csnet@csnet-relay.arpa
  201. X * brachman@ubc.csnet
  202. X * ========================================================================
  203. X */
  204. X
  205. X#include <sys/file.h>
  206. X#include <ctype.h>
  207. X#include <pwd.h>
  208. X#include <stdio.h>
  209. X
  210. X#define min(a, b)        ((a) < (b) ? (a) : (b))
  211. X
  212. X/*
  213. X * Configurable...
  214. X * BUFOUT should be fairly large
  215. X */
  216. X#define BUFIN                1024    /* maximum length of an input line */
  217. X#define BUFOUT                (BUFIN * 5)
  218. X#define MAXPAGES            10000    /* maximum number of pages per job */
  219. X#define DEFAULT_TAB_SIZE    8
  220. X#define DEFAULT_POINT_SIZE    10
  221. X#ifndef PROLOGUE
  222. X#define PROLOGUE            "/usr/local/lib/lwf.prologue"
  223. X#endif
  224. X#ifndef REVERSE
  225. X#define REVERSE                0
  226. X#endif
  227. X#ifndef PR
  228. X#define PR                    "/bin/pr"
  229. X#endif
  230. X
  231. X#ifdef SYSV
  232. X#define rindex                strrchr
  233. X#endif
  234. X
  235. X/*
  236. X * As mentioned in the man page, /bin/pr doesn't handle formfeeds correctly
  237. X * when doing multicolumn formatting
  238. X * Instead of starting a new column or page it passes a formfeed through,
  239. X * causing pr and lwf to get out-of-synch with regard to the current
  240. X * location on the page
  241. X * If your pr behaves this way (4.[23] does, SYSV doesn't), define PRBUG so
  242. X * that fgetline() will filter out these bogus formfeeds while preserving
  243. X * the column structuring
  244. X */
  245. X#ifndef SYSV
  246. X#define PRBUG                1
  247. X#endif
  248. X
  249. X/*
  250. X * PostScript command strings defined in the prologue file
  251. X */
  252. X#define BACKSPACE        "B"
  253. X#define ENDPAGE            "EP"
  254. X#define LINETO            "L"
  255. X#define MOVETO            "M"
  256. X#define NEWPATH            "NP"
  257. X#define SHOW            "S"
  258. X#define STARTPAGE        "SP"
  259. X#define STARTHPAGE        "SHP"
  260. X#define STARTLPAGE        "SLP"
  261. X#define STROKE            "ST"
  262. X#define TAB                "T"
  263. X
  264. X/*
  265. X * Conformance requires that no PostScript line exceed 256 characters
  266. X */
  267. X#define MAX_OUTPUT_LINE_LENGTH    256
  268. X
  269. X#define TEXTFONT        "Courier"
  270. X#define HEADERFONT        "Times-Roman"
  271. X#define HEADERPS        18            /* header page point size */
  272. X
  273. X#define PORTRAIT_START_Y    768        /* first row (Y coord) on each page */
  274. X#define LANDSCAPE_START_Y    576
  275. X#define START_X                25        /* position of start of each line */
  276. X#define START_Y_HEADER        700        /* first row (Y coord) of header */
  277. X#define THREE_HOLE_X        1.0        /* portrait x offset (inches) 3 hole */
  278. X#define THREE_HOLE_Y        0.5        /* landscape y offset (inches) 3 hole */
  279. X
  280. X#define MAX_X            612
  281. X#define MAX_Y            792
  282. X
  283. X#define SEP_CHAR        '\001'        /* pr column separator character */
  284. X
  285. X#define PS_EOF            04
  286. X
  287. X#define NPSIZES            6
  288. struct psize {
  289. X    int size;                        /* point size */
  290. X    double charsperinch;            /* approx. char width, for Courier */
  291. X    int portrait_page_length;        /* page length in lines */
  292. X    int portrait_cols;                /* maximum # of chars per line */
  293. X    int landscape_page_length;
  294. X    int landscape_cols;
  295. X} psize[NPSIZES] = {
  296. X     7, 17.0, 108, 135, 80, 181,
  297. X     8, 15.0,  94, 118, 70, 159,
  298. X     9, 14.0,  84, 105, 62, 141,
  299. X    10, 12.0,  75,  94, 56, 127,
  300. X    11, 11.0,  68,  86, 51, 115,
  301. X    12, 10.0,  62,  79, 46, 106
  302. X};
  303. X
  304. X#define USAGE    \
  305. X"[-d] [-i#] [-l] [-m] [-olist] [-p[str]] [-r] [-s#] [-t#] [-v] [-S] [file ...]"
  306. X
  307. long page_map[MAXPAGES];    /* offset of first byte of each page */
  308. int page_count;
  309. X
  310. int lines_per_page;
  311. int columns;
  312. int point_size;
  313. int start_x, start_y;
  314. int ncopies;
  315. X
  316. char bufin[BUFIN];            /* input buffer */
  317. char bufout[BUFOUT];        /* used for page reversal and output buffering */
  318. X
  319. char *currentdate, *username;
  320. char hostname[32];
  321. X
  322. int row;
  323. char *range;
  324. int tabstop;
  325. char *propts;
  326. X
  327. int dflag, lflag, mflag, pflag, rflag, vflag, Sflag;
  328. X
  329. char *strcpy();
  330. char *fgetline();
  331. char *sprintf();
  332. X
  333. char *prologue;
  334. char *progname;
  335. X
  336. char *version = "lwf V2.0 brachman@ubc.csnet 21-Feb-88";
  337. X
  338. main(argc, argv)
  339. int argc;
  340. char **argv;
  341. X{
  342. X    register int i, j, first_file;
  343. X    char *pc;
  344. X    struct psize *p, *get_psize();
  345. X    double offset, atof();
  346. X    char *rindex();
  347. X    FILE *infile, *popen();
  348. X    double ceil();
  349. X
  350. X    if ((pc = rindex(argv[0], '/')) != (char *) NULL)
  351. X        progname = pc + 1;
  352. X    else
  353. X        progname = argv[0];
  354. X    range = ":";
  355. X    propts = "";
  356. X    tabstop = DEFAULT_TAB_SIZE;
  357. X    page_count = 0;
  358. X    ncopies = 1;
  359. X    offset = 0.0;
  360. X    prologue = PROLOGUE;
  361. X    p = get_psize(DEFAULT_POINT_SIZE);
  362. X    rflag = REVERSE;
  363. X
  364. X    for (i = 1; i < argc && argv[i][0] == '-'; i++) {
  365. X        switch (argv[i][1]) {
  366. X        case 'c':
  367. X            ncopies = atof(&argv[i][2]);
  368. X            if (ncopies <= 0) {
  369. X                fatal("number of copies must be > 0");
  370. X                /*NOTREACHED*/
  371. X            }
  372. X            break;
  373. X        case 'd':
  374. X            dflag = 1;
  375. X            break;
  376. X        case 'i':
  377. X            offset = atof(&argv[i][2]);
  378. X            if (offset < 0.0 || offset >= 8.5) {
  379. X                fatal("bad indent");
  380. X                /*NOTREACHED*/
  381. X            }
  382. X            break;
  383. X        case 'l':
  384. X            lflag = 1;
  385. X            break;
  386. X        case 'm':
  387. X            mflag = 1;
  388. X            break;
  389. X        case 'o':
  390. X            range = &argv[i][2];
  391. X            if (checkrange(range)) {
  392. X                fatal("bad range specification");
  393. X                /*NOTREACHED*/
  394. X            }
  395. X            break;
  396. X        case 'p':
  397. X            pflag = 1;
  398. X            propts = &argv[i][2];
  399. X            break;
  400. X        case 'P':
  401. X            if (++i == argc) {
  402. X                fatal("missing filename after -P");
  403. X                /*NOTREACHED*/
  404. X            }
  405. X            prologue = argv[i];
  406. X            break;
  407. X        case 'r':
  408. X            rflag = !rflag;
  409. X            break;
  410. X        case 's':
  411. X            j = atoi(&argv[i][2]);
  412. X            if ((p = get_psize(j)) == (struct psize *) NULL) {
  413. X                fatal("bad point size");
  414. X                /*NOTREACHED*/
  415. X            }
  416. X            break;
  417. X        case 't':
  418. X            tabstop = atoi(&argv[i][2]);
  419. X            if (tabstop < 1) {
  420. X                fatal("bad tabstop");
  421. X                /*NOTREACHED*/
  422. X            }
  423. X            break;
  424. X        case 'v':
  425. X            vflag = 1;
  426. X            break;
  427. X        case 'S':
  428. X            Sflag = 1;
  429. X            break;
  430. X        default:
  431. X            (void) fprintf(stderr, "Usage: %s %s\n", progname, USAGE);
  432. X            exit(1);
  433. X        }
  434. X    }
  435. X
  436. X    /*
  437. X     * Check that all files are readable
  438. X     * This is so that no output at all is produced if any file is not
  439. X     * readable in case the output is being piped to a printer
  440. X     */
  441. X    for (j = i; j < argc; j++) {
  442. X        if (access(argv[j], R_OK) == -1) {
  443. X            fatal("cannot access %s", argv[j]);
  444. X            /*NOTREACHED*/
  445. X        }
  446. X    }
  447. X
  448. X    point_size = p->size;
  449. X
  450. X    if (lflag) {
  451. X        start_y = LANDSCAPE_START_Y;
  452. X        start_x = START_X + (int) (offset * 72.27);
  453. X        lines_per_page = p->landscape_page_length;
  454. X        columns = p->landscape_cols - (int) ceil(offset * p->charsperinch);
  455. X        if (mflag) {
  456. X            int nlines;
  457. X
  458. X            nlines = (int) ceil((THREE_HOLE_Y * 72.27) / point_size);
  459. X            start_y -= (nlines * point_size);
  460. X            lines_per_page -= nlines;
  461. X            columns -= (int) ceil(THREE_HOLE_Y * p->charsperinch);
  462. X        }
  463. X    }
  464. X    else {
  465. X        start_y = PORTRAIT_START_Y;
  466. X        lines_per_page = p->portrait_page_length;
  467. X        start_x = START_X;
  468. X        if (mflag)
  469. X                offset += THREE_HOLE_X;
  470. X        start_x += (int) (offset * 72.27);
  471. X        columns = p->portrait_cols - (int) ceil(offset * p->charsperinch);
  472. X    }
  473. X    if (vflag) {
  474. X        (void) fprintf(stderr, "%s\n\n", version);
  475. X        (void) fprintf(stderr, "Lines/page = %d\n", lines_per_page);
  476. X        (void) fprintf(stderr, "Columns = %d\n", columns);
  477. X        (void) fprintf(stderr, "X-offset = %5.2f inches\n", offset);
  478. X    }
  479. X
  480. X    setup();
  481. X    preamble();
  482. X
  483. X    first_file = i;
  484. X
  485. X    if (!rflag && Sflag)
  486. X        header(argc - first_file, argv + first_file);
  487. X
  488. X    if (i == argc) {    /* no files on command line */
  489. X        infile = stdin;
  490. X        if (pflag) {
  491. X            build_prcmd(bufin, "");
  492. X            if ((infile = popen(bufin, "r")) == (FILE *) NULL) {
  493. X                fatal("popen failed");
  494. X                /*NOTREACHED*/
  495. X            }
  496. X        }
  497. X        if (vflag)
  498. X            (void) fprintf(stderr, "printing stdin\n");
  499. X        print(infile);
  500. X        if (pflag)
  501. X            (void) pclose(infile);
  502. X    }
  503. X
  504. X    /*
  505. X     * If page reversal is performed, process the file arguments right to left,
  506. X     * oth. left to right
  507. X     * If the correct flag is used for the printer the first file argument
  508. X     * will be on top in the printer's output tray when the paper is removed
  509. X     */
  510. X    if (rflag)
  511. X        j = argc - 1;
  512. X    else
  513. X        j = i;
  514. X    while (i < argc) {
  515. X        infile = stdin;
  516. X        if (pflag) {
  517. X            build_prcmd(bufin, argv[j]);
  518. X            if ((infile = popen(bufin, "r")) == (FILE *) NULL) {
  519. X                fatal("popen failed");
  520. X                /*NOTREACHED*/
  521. X            }
  522. X        }
  523. X        else {
  524. X            if (freopen(argv[j], "r", stdin) == (FILE *) NULL) {
  525. X                fatal("can't open %s", argv[j]);
  526. X                /*NOTREACHED*/
  527. X            }
  528. X        }
  529. X        if (vflag)
  530. X            (void) fprintf(stderr, "printing %s\n", argv[j]);
  531. X        print(infile);
  532. X        if (pflag)
  533. X            (void) pclose(infile);
  534. X        if (rflag)
  535. X            j--;
  536. X        else
  537. X            j++;
  538. X        i++;
  539. X    }
  540. X
  541. X    if (rflag && Sflag)
  542. X        header(argc - first_file, argv + first_file);
  543. X
  544. X    (void) printf("%%%%Trailer\n");
  545. X    (void) printf("%%%%Pages: %d\n", page_count);
  546. X    if (Sflag)
  547. X        (void) putc(PS_EOF, stdout);
  548. X
  549. X    if (fflush(stdout) == EOF) {
  550. X        fatal("write error on stdout");
  551. X        /*NOTREACHED*/
  552. X    }
  553. X    exit(0);
  554. X}
  555. X
  556. X/*
  557. X * Return a pointer to the point size structure for the
  558. X * specified point size
  559. X */
  560. struct psize *
  561. get_psize(size)
  562. int size;
  563. X{
  564. X    register int i;
  565. X
  566. X    for (i = 0; i < NPSIZES; i++)
  567. X        if (psize[i].size == size)
  568. X            break;
  569. X    if (i == NPSIZES)
  570. X        return((struct psize *) NULL);
  571. X    return(&psize[i]);
  572. X}
  573. X
  574. X/*
  575. X * Initial lines sent to the LaserWriter
  576. X * This stuff is sent to stdout since we don't want it to be reversed
  577. X * Generates the PostScript header and includes the prologue file
  578. X * There is limited checking for I/O errors here
  579. X * When the standard prologue is being used we probably should verify
  580. X * that it is the correct version (via %%BeginProcSet)
  581. X */
  582. preamble()
  583. X{
  584. X    FILE *fp;
  585. X
  586. X    if ((fp = fopen(prologue, "r")) == (FILE *) NULL) {
  587. X        fatal("can't open prologue file `%s'", prologue);
  588. X        /*NOTREACHED*/
  589. X    }
  590. X
  591. X    if (Sflag)
  592. X        (void) putc(PS_EOF, stdout);
  593. X
  594. X    (void) printf("%%!PS-Adobe-2.0\n");
  595. X    (void) printf("%%%%Creator: %s on %s\n", progname, hostname);
  596. X    (void) printf("%%%%CreationDate: %s\n", currentdate);
  597. X    (void) printf("%%%%For: %s\n", username);
  598. X    (void) printf("%%%%DocumentFonts: %s", TEXTFONT);
  599. X    if (Sflag)
  600. X        (void) printf(" %s\n", HEADERFONT);
  601. X    else
  602. X        (void) printf("\n");
  603. X    (void) printf("%%%%Pages: (atend)\n");
  604. X
  605. X    while (fgets(bufin, sizeof(bufin), fp) != (char *) NULL)
  606. X        fputs(bufin, stdout);
  607. X    (void) fclose(fp);
  608. X    if (ferror(stdout) || fflush(stdout) == EOF) {
  609. X        fatal("write error on stdout");
  610. X        /*NOTREACHED*/
  611. X    }
  612. X}
  613. X
  614. X/*
  615. X * Generate a command, in the specified buffer, to print the given file
  616. X * according to the options in effect
  617. X */
  618. build_prcmd(buf, file)
  619. char *buf, *file;
  620. X{
  621. X
  622. X#ifdef SYSV
  623. X    (void) sprintf(buf, "%s -e%d -w%d -l%d -s%c %s %s",
  624. X                PR, tabstop, columns, lines_per_page, SEP_CHAR, propts, file);
  625. X#else
  626. X    (void) sprintf(buf, "%s -w%d -l%d -s%c %s %s",
  627. X                        PR, columns, lines_per_page, SEP_CHAR, propts, file);
  628. X#endif
  629. X    if (vflag)
  630. X        (void) fprintf(stderr, "pr cmd: %s\n", buf);
  631. X}
  632. X
  633. X/*
  634. X * Print a file
  635. X *
  636. X * The input stream may be stdin, a file, or a pipe
  637. X * If page reversal is being performed, the output goes to a temporary file and
  638. X * then reverse() is called to do the page reversal to stdout
  639. X */
  640. print(infile)
  641. XFILE *infile;
  642. X{
  643. X    register int eof, pagenum, r;
  644. X    register char *p;
  645. X    FILE *outfile;
  646. X    char *mktemp();
  647. X
  648. X    if (rflag) {
  649. X        static char bigbuf[BUFOUT];
  650. X
  651. X        page_map[0] = 0L;
  652. X        (void) sprintf(bufin, "/tmp/%sXXXXXX", progname);
  653. X        if (vflag)
  654. X            (void) fprintf(stderr, "temp will be: %s ... ", bufin);
  655. X        p = mktemp(bufin);
  656. X        if (vflag)
  657. X            (void) fprintf(stderr, "%s\n", p);
  658. X        if ((outfile = fopen(p, "w+")) == (FILE *) NULL) {
  659. X            (void) fprintf(stderr, "%s: can't create %s\n", progname, p);
  660. X            cleanup();
  661. X            /*NOTREACHED*/
  662. X        }
  663. X        setbuffer(outfile, bigbuf, sizeof(bigbuf));
  664. X        if (!dflag)
  665. X            (void) unlink(p);
  666. X        else
  667. X            (void) fprintf(stderr, "will not unlink %s\n", p);
  668. X    }
  669. X    else
  670. X        outfile = stdout;
  671. X
  672. X    pagenum = 1;
  673. X    eof = 0;
  674. X    while (!eof) {
  675. X        row = start_y;
  676. X        if ((r = inrange(pagenum, range)) == -1) {
  677. X            cleanup();
  678. X            /*NOTREACHED*/
  679. X        }
  680. X        else if (r == 1)
  681. X            eof = printpage(infile, outfile);
  682. X        else if (r == 0)
  683. X            eof = flushpage(infile);
  684. X        else {
  685. X            fatal("bad inrange result");
  686. X            /*NOTREACHED*/
  687. X        }
  688. X        pagenum++;
  689. X    }
  690. X    if (row != start_y)
  691. X        endpage(outfile);
  692. X    if (vflag)
  693. X        (void) fprintf(stderr, "\n");
  694. X    if (fflush(outfile) == EOF) {
  695. X        fatal("write error while flushing output");
  696. X        /*NOTREACHED*/
  697. X    }
  698. X    if (rflag) {
  699. X        reverse(outfile);
  700. X        (void) fclose(outfile);
  701. X    }
  702. X}
  703. X
  704. X/*
  705. X * Process the next page
  706. X * Return 1 on EOF, 0 oth.
  707. X */
  708. printpage(infile, outfile)
  709. XFILE *infile, *outfile;
  710. X{
  711. X    register int lineno;
  712. X
  713. X    if (ungetc(getc(infile), infile) == EOF)
  714. X        return(1);
  715. X
  716. X    startpage(page_count + 1, outfile);
  717. X    for (lineno = 0; lineno < lines_per_page; lineno++) {
  718. X        if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL)
  719. X            return(1);
  720. X        if (bufin[0] == '\f')
  721. X            break;
  722. X        if (bufin[0] != '\0') {
  723. X            (void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
  724. X            proc(bufin, outfile);
  725. X        }
  726. X        row -= point_size;
  727. X    }
  728. X    endpage(outfile);
  729. X    return(0);
  730. X}
  731. X
  732. X/*
  733. X * The next page will not be printed; just consume the input and discard
  734. X * Don't change xrow since we don't want an endpage()
  735. X */
  736. flushpage(infile)
  737. XFILE *infile;
  738. X{
  739. X    register int lineno, xrow;
  740. X
  741. X    xrow = row;
  742. X    for (lineno = 0; lineno < lines_per_page; lineno++) {
  743. X        if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL)
  744. X            return(1);
  745. X        if (bufin[0] == '\f')
  746. X            break;
  747. X        xrow -= point_size;
  748. X    }
  749. X    return(0);
  750. X}
  751. X
  752. X/*
  753. X * Start a new page
  754. X */
  755. startpage(n, outfile)
  756. int n;
  757. XFILE *outfile;
  758. X{
  759. X
  760. X    (void) fprintf(outfile, "%%%%Page: ? %d\n", n);
  761. X    (void) fprintf(outfile, "%d /%s %s\n",
  762. X            point_size, TEXTFONT, lflag ? STARTLPAGE : STARTPAGE);
  763. X}
  764. X
  765. X/*
  766. X * A page has been written to the temp file
  767. X * Record the start of the next page
  768. X * Terminate the page and indicate the start of the next
  769. X */
  770. endpage(outfile)
  771. XFILE *outfile;
  772. X{
  773. X    long ftell();
  774. X
  775. X    if (page_count == MAXPAGES) {
  776. X        fatal("pagelimit (%d) reached", MAXPAGES);
  777. X        /*NOTREACHED*/
  778. X    }
  779. X    (void) fprintf(outfile, "%d %s\n", ncopies, ENDPAGE);
  780. X    if (rflag) {
  781. X        if (fflush(outfile) == EOF) {
  782. X            fatal("write error while flushing page");
  783. X            /*NOTREACHED*/
  784. X        }
  785. X        page_map[++page_count] = ftell(outfile);
  786. X    }
  787. X    else
  788. X        page_count++;
  789. X    if (vflag)
  790. X        (void) fprintf(stderr, "x");
  791. X}
  792. X
  793. X/*
  794. X * Print the pages to stdout in reverse order
  795. X * Assumes that the number of characters per page can be contained in an int
  796. X */
  797. reverse(outfile)
  798. XFILE *outfile;
  799. X{
  800. X    register int i;
  801. X    int bytecount, nbytes;
  802. X    long lseek();
  803. X
  804. X    if (vflag)
  805. X        (void) fprintf(stderr, "\nreversing %d page%s\n", page_count,
  806. X                        page_count > 1 ? "s" : "");
  807. X    if (dflag) {
  808. X        for (i = 0; i <= page_count; i++)
  809. X            (void) fprintf(stderr, "[%ld]\n", page_map[i]);
  810. X    }
  811. X    for (i = page_count - 1; i >= 0; i--) {
  812. X        if (fseek(outfile, page_map[i], 0) == -1L) {
  813. X            fatal("seek error");
  814. X            /*NOTREACHED*/
  815. X        }
  816. X        nbytes = (int) (page_map[i + 1] - page_map[i]);
  817. X        while (nbytes > 0) {
  818. X            bytecount = min(nbytes, sizeof(bufout));
  819. X            if (fread(bufout, 1, bytecount, outfile) != bytecount) {
  820. X                fatal("read error while reversing pages");
  821. X                /*NOTREACHED*/
  822. X            }
  823. X            if (fwrite(bufout, 1, bytecount, stdout) != bytecount) {
  824. X                fatal("write error while reversing pages");
  825. X                /*NOTREACHED*/
  826. X            }
  827. X            nbytes -= bytecount;
  828. X        }
  829. X    }
  830. X}
  831. X
  832. X/*
  833. X * Process a line of input, escaping characters when necessary and handling
  834. X * tabs
  835. X *
  836. X * The output is improved somewhat by coalescing consecutive tabs and
  837. X * backspaces and eliminating tabs at the end of a line
  838. X *
  839. X * Overprinting (presumably most often used in underlining) can be far from
  840. X * optimal; in particular the way nroff underlines by sequences like
  841. X * "_\ba_\bb_\bc" creates a large volume of PostScript.  This isn't too
  842. X * serious since a lot of nroff underlining is unlikely.
  843. X *
  844. X * Since a newline is generated for each call there will be more
  845. X * newlines in the output than is necessary
  846. X */
  847. proc(in, outfile)
  848. char *in;
  849. XFILE *outfile;
  850. X{
  851. X    register int i;
  852. X    register char *last, *p, *q;
  853. X    int currentp, instr, tabc, tabto;
  854. X    char *savep;
  855. X    static int colskip, ncols;
  856. X    static int seen_sep = 0;
  857. X
  858. X    currentp = 0;
  859. X    instr = 0;
  860. X    tabto = 0;
  861. X    last = bufout + MAX_OUTPUT_LINE_LENGTH - 20;    /* subtract slop factor */
  862. X
  863. X    q = bufout;
  864. X    *q = '\0';
  865. X    for (p = in; *p != '\0'; p++) {
  866. X        switch (*p) {
  867. X        case SEP_CHAR:
  868. X            /*
  869. X             * This assumes that the input buffer contains the entire line
  870. X             * oth. the column count will be off
  871. X             * Also, the input stream must be formatted into a constant number
  872. X             * of columns oth. it would be necessary to scan each line to
  873. X             * count SEP_CHARs (which is not hard but could be slow)
  874. X             */
  875. X            if (!seen_sep) {            /* discern number of columns */
  876. X                seen_sep = 1;
  877. X                ncols = 2;                /* there are at least two columns... */
  878. X                savep = p++;
  879. X                while (*p != '\0') {
  880. X                    if (*p++ == SEP_CHAR)
  881. X                        ncols++;
  882. X                }
  883. X                p = savep;
  884. X                colskip = columns / ncols;
  885. X                if (vflag)
  886. X                    (void) fprintf(stderr, "Using %d columns\n", ncols);
  887. X            }
  888. X            if (instr) {
  889. X                (void) sprintf(q, ")%s ", SHOW);
  890. X                q += strlen(q);
  891. X                instr = 0;
  892. X            }
  893. X            tabto += (colskip - currentp);
  894. X            currentp = 0;
  895. X            break;
  896. X        case '\t':
  897. X            /*
  898. X             * Count the number of tabs that immediately follow the one we're
  899. X             * looking at
  900. X             */
  901. X            tabc = 0;
  902. X            while (*(p + 1) == '\t') {
  903. X                p++;
  904. X                tabc++;
  905. X            }
  906. X            if (currentp > 0) {        /* not beginning of line */
  907. X                i = tabstop - (currentp % tabstop) + tabc * tabstop;
  908. X                if (instr) {
  909. X                    (void) sprintf(q, ")%s ", SHOW);
  910. X                    q += strlen(q);
  911. X                    instr = 0;
  912. X                }
  913. X            }
  914. X            else
  915. X                i = (tabc + 1) * tabstop;
  916. X            tabto += i;
  917. X            currentp += i;
  918. X            break;
  919. X        case '\b':
  920. X            *q = '\0';
  921. X            (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  922. X            /* backspacing over tabs doesn't work... */
  923. X            if (tabto != 0) {
  924. X                fatal("attempt to backspace over a tab");
  925. X                /*NOTREACHED*/
  926. X            }
  927. X            p++;
  928. X            for (i = 1; *p == '\b'; p++)
  929. X                i++;
  930. X            if (currentp - i < 0) {
  931. X                fatal("too many backspaces");
  932. X                /*NOTREACHED*/
  933. X            }
  934. X            if (!instr) {
  935. X                fatal("bad backspacing");
  936. X                /*NOTREACHED*/
  937. X            }
  938. X            if (i == 1)                /* frequent case gets special attention */
  939. X                (void) sprintf(bufout, "%s (", BACKSPACE);
  940. X            else
  941. X                (void) sprintf(bufout, "-%d %s (", i, TAB);
  942. X            currentp -= i;
  943. X            q = bufout + strlen(bufout);
  944. X            p--;
  945. X            break;
  946. X        case '\f':
  947. X            tabto = 0;                            /* optimizes */
  948. X            *q = '\0';
  949. X            if (instr)
  950. X                (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  951. X            else
  952. X                (void) fprintf(outfile, "%s\n", bufout);
  953. X            endpage(outfile);
  954. X            startpage(page_count + 1, outfile);
  955. X            row = start_y;
  956. X            (void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
  957. X            q = bufout;
  958. X            currentp = 0;
  959. X            instr = 0;
  960. X            break;
  961. X        case '\r':
  962. X            tabto = 0;                            /* optimizes */
  963. X            if (instr) {
  964. X                *q = '\0';
  965. X                (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  966. X                instr = 0;
  967. X                q = bufout;
  968. X            }
  969. X            (void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
  970. X            currentp = 0;
  971. X            break;
  972. X        case '\\':
  973. X        case '(':
  974. X        case ')':
  975. X            if (!instr) {
  976. X                if (tabto) {
  977. X                    (void) sprintf(q, "%d %s ", tabto, TAB);
  978. X                    q += strlen(q);
  979. X                    tabto = 0;
  980. X                }
  981. X                *q++ = '(';
  982. X                instr = 1;
  983. X            }
  984. X            *q++ = '\\';
  985. X            *q++ = *p;
  986. X            currentp++;
  987. X            break;
  988. X        default:
  989. X            /*
  990. X             * According to the PostScript Language Manual, PostScript files
  991. X             * can contain only "the printable subset of the ASCII character
  992. X             * set (plus the newline marker)".
  993. X             */
  994. X            if (!isascii(*p) || !isprint(*p)) {
  995. X                fatal("bad character in input");
  996. X                /*NOTREACHED*/
  997. X            }
  998. X            if (!instr) {
  999. X                if (tabto) {
  1000. X                    (void) sprintf(q, "%d %s ", tabto, TAB);
  1001. X                    q += strlen(q);
  1002. X                    tabto = 0;
  1003. X                }
  1004. X                *q++ = '(';
  1005. X                instr = 1;
  1006. X            }
  1007. X            *q++ = *p;
  1008. X            currentp++;
  1009. X            break;
  1010. X        }
  1011. X        if (q >= last) {
  1012. X            *q = '\0';
  1013. X            if (instr)
  1014. X                (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  1015. X            else
  1016. X                (void) fprintf(outfile, "%s\n", bufout);
  1017. X            q = bufout;
  1018. X            instr = 0;
  1019. X        }
  1020. X    }
  1021. X    if (instr) {
  1022. X        (void) sprintf(q, ")%s", SHOW);
  1023. X        q += strlen(q);
  1024. X    }
  1025. X    else
  1026. X        *q = '\0';
  1027. X    if (q >= last) {
  1028. X        fatal("bufout overflow");
  1029. X        /*NOTREACHED*/
  1030. X    }
  1031. X    if (bufout[0] != '\0')
  1032. X        (void) fprintf(outfile, "%s\n", bufout);
  1033. X}
  1034. X
  1035. X/*
  1036. X * Find out who the user is, etc.
  1037. X * Possible system dependencies here...
  1038. X */
  1039. setup()
  1040. X{
  1041. X    int len;
  1042. X    char *p;
  1043. X    long t, time();
  1044. X    int gethostname();
  1045. X    char *ctime(), *getlogin(), *malloc();
  1046. X    struct passwd *pw, *getpwuid();
  1047. X
  1048. X    if ((p = getlogin()) == (char *) NULL) {
  1049. X        if ((pw = getpwuid(getuid())) == (struct passwd *) NULL)
  1050. X            p = "Whoknows";
  1051. X        else
  1052. X            p = pw->pw_name;
  1053. X        endpwent();
  1054. X    }
  1055. X    username = (char *) malloc((unsigned) (strlen(p) + 1));
  1056. X    (void) strcpy(username, p);
  1057. X
  1058. X#ifdef HOSTNAME
  1059. X    (void) strncpy(hostname, HOSTNAME, sizeof(hostname));
  1060. X    hostname[sizeof(hostname) - 1] = '\0';
  1061. X#else
  1062. X    (void) gethostname(hostname, sizeof(hostname));
  1063. X#endif
  1064. X
  1065. X    t = time((long *) 0);
  1066. X    p = ctime(&t);
  1067. X    len = strlen(p);
  1068. X    *(p + len - 1) = '\0';        /* zap the newline character */
  1069. X    currentdate = (char *) malloc((unsigned) len);
  1070. X    (void) strcpy(currentdate, p);
  1071. X}
  1072. X
  1073. X/*
  1074. X * Print a header page
  1075. X * Assumes setup() has already been called to fill in the user, host, etc.
  1076. X * Uses HEADERFONT in HEADERPS point
  1077. X */
  1078. header(nfiles, files)
  1079. int nfiles;
  1080. char **files;
  1081. X{
  1082. X    register int i;
  1083. X    register char *p;
  1084. X
  1085. X    if (vflag) {
  1086. X        (void) fprintf(stderr, "printing header\n");
  1087. X        (void) fprintf(stderr, "%d file%s are:\n", nfiles,
  1088. X                            nfiles > 1 ? "s" : "");
  1089. X        if (nfiles == 0)
  1090. X            (void) fprintf(stderr, "\tstdin\n");
  1091. X        for (i = 0; i < nfiles; i++)
  1092. X            (void) fprintf(stderr, "\t%s\n", files[i]);
  1093. X    }
  1094. X
  1095. X    (void) fprintf(stdout, "%%%%Page: ? %d\n", ++page_count);
  1096. X    (void) fprintf(stdout, "%d /%s %s\n", HEADERPS, HEADERFONT, STARTHPAGE);
  1097. X
  1098. X    /*
  1099. X     * The header sheet looks like:
  1100. X     *
  1101. X     * ----------------------------
  1102. X     * ----------------------------
  1103. X     *
  1104. X     * User:
  1105. X     * Host:
  1106. X     * Date:
  1107. X     * Files:
  1108. X     *
  1109. X     * ----------------------------
  1110. X     * ----------------------------
  1111. X     */
  1112. X    row = START_Y_HEADER;
  1113. X    (void) printf("%s %d %d %s\n", NEWPATH, START_X, row, MOVETO);
  1114. X    (void) printf("%d %d %s\n", START_X + 400, row, LINETO);
  1115. X    row -= 6;
  1116. X    (void) printf("%d %d %s\n", START_X, row, MOVETO);
  1117. X    (void) printf("%d %d %s\n", START_X + 400, row, LINETO);
  1118. X    row -= 24;
  1119. X    (void) printf("%s\n", STROKE);
  1120. X
  1121. X    (void) printf("%d %d %s\n", START_X, row, MOVETO);
  1122. X    (void) sprintf(bufin, "User: %s", username);
  1123. X    proc(bufin, stdout);
  1124. X    row -= 24;
  1125. X    (void) printf("%d %d %s\n", START_X, row, MOVETO);
  1126. X    (void) sprintf(bufin, "Host: %s", hostname);
  1127. X    proc(bufin, stdout);
  1128. X    row -= 24;
  1129. X    (void) printf("%d %d %s\n", START_X, row, MOVETO);
  1130. X    (void) sprintf(bufin, "Date: %s", currentdate);
  1131. X    proc(bufin, stdout);
  1132. X    row -= 24;
  1133. X
  1134. X    if (nfiles == 0) {
  1135. X        (void) printf("%d %d %s\n", START_X, row, MOVETO);
  1136. X        (void) sprintf(bufin, "File: <stdin>");
  1137. X        proc(bufin, stdout);
  1138. X    }
  1139. X    else {
  1140. X        register int len, max, sum;
  1141. X
  1142. X        /*
  1143. X         * If the list of files is "too long" we'll only print as many as
  1144. X         * possible
  1145. X         * Arbitrary chop off point is 50 characters
  1146. X         * (assume bufin is bigger than this)
  1147. X         */
  1148. X        (void) printf("%d %d %s\n", START_X, row, MOVETO);
  1149. X        (void) sprintf(bufin, "File%s: ", nfiles > 1 ? "s" : "");
  1150. X        p = bufin + (sum = strlen(bufin));
  1151. X        max = 50;
  1152. X        for (i = 0; i < nfiles - 1; i++) {
  1153. X            sum += (len = strlen(files[i]) + 1);
  1154. X            if (sum >= max)
  1155. X                break;
  1156. X            (void) sprintf(p, "%s,", files[i]);
  1157. X            p += len;
  1158. X        }
  1159. X        sum += (len = strlen(files[i]) + 1);
  1160. X        if (sum < max)
  1161. X            (void) sprintf(p, "%s", files[i]);
  1162. X        else
  1163. X            (void) strcpy(p, "...");
  1164. X        proc(bufin, stdout);
  1165. X    }
  1166. X
  1167. X    row -= 12;
  1168. X    (void) printf("%s %d %d %s\n", NEWPATH, START_X, row, MOVETO);
  1169. X    (void) printf("%d %d %s\n", START_X + 400, row, LINETO);
  1170. X    row -= 6;
  1171. X    (void) printf("%d %d %s\n", START_X, row, MOVETO);
  1172. X    (void) printf("%d %d %s\n", START_X + 400, row, LINETO);
  1173. X    (void) printf("%s\n", STROKE);
  1174. X    (void) printf("1 %s\n", ENDPAGE);
  1175. X    if (fflush(stdout) == EOF) {
  1176. X        fatal("write error on stdout");
  1177. X        /*NOTREACHED*/
  1178. X    }
  1179. X}
  1180. X
  1181. X/*
  1182. X * Special version of fgets
  1183. X * Read until a formfeed, newline, or overflow
  1184. X * If a formfeed is the first character, return it immediately
  1185. X * If a formfeed is found after the first character, replace it by a newline
  1186. X * and push the formfeed back onto the input stream
  1187. X * A special case is a formfeed followed by a newline in which case the
  1188. X * newline is ignored 
  1189. X * The input buffer will be null-terminated and will *not* end with a newline
  1190. X * The buffer size n includes the null
  1191. X */
  1192. char *
  1193. fgetline(s, n, iop)
  1194. char *s;
  1195. int n;
  1196. register FILE *iop;
  1197. X{
  1198. X    register int ch;
  1199. X    register char *cs;
  1200. X
  1201. X    if (n < 2) {
  1202. X        fatal("fgetline called with bad buffer size!?");
  1203. X        /*NOTREACHED*/
  1204. X    }
  1205. X
  1206. X    cs = s;
  1207. X    n--;                                /* the null */
  1208. X
  1209. X    /*
  1210. X     * Check out the special cases
  1211. X     */
  1212. X    if ((ch = getc(iop)) == EOF)
  1213. X        return((char *) NULL);
  1214. X    if (ch == '\f') {
  1215. X#ifdef PRBUG
  1216. X        if (pflag) {
  1217. X            /*
  1218. X             * Filter out the formfeeds
  1219. X             */
  1220. X            do {
  1221. X                if (ch == '\f')
  1222. X                    continue;
  1223. X                if (ch == '\n')
  1224. X                    break;
  1225. X                *cs++ = ch;
  1226. X                n--;
  1227. X            } while (n > 0 && (ch = getc(iop)) != EOF);
  1228. X            if (ch == EOF) {
  1229. X                if (ungetc(ch, iop) == EOF && !feof(iop)) {
  1230. X                    /* Shouldn't happen since a getc() was just done */
  1231. X                    fatal("fgetline - ungetc failed");
  1232. X                    /*NOTREACHED*/
  1233. X                }
  1234. X            }
  1235. X            else if (ch != '\n') {
  1236. X                fatal("fgetline - input line too long");
  1237. X                /*NOTREACHED*/
  1238. X            }
  1239. X            *cs = '\0';
  1240. X            return(s);
  1241. X        }
  1242. X#endif
  1243. X        if ((ch = getc(iop)) != '\n') {
  1244. X            /*
  1245. X             * If EOF was just read it will be noticed next time through
  1246. X             */
  1247. X            if (ungetc(ch, iop) == EOF && !feof(iop)) {
  1248. X                /* Shouldn't happen since a getc() was just done */
  1249. X                fatal("fgetline - ungetc failed");
  1250. X                /*NOTREACHED*/
  1251. X            }
  1252. X        }
  1253. X        *cs++ = '\f';
  1254. X        *cs = '\0';
  1255. X        return(s);
  1256. X    }
  1257. X
  1258. X    /*
  1259. X     * Check for "weird" input characters is made in proc()
  1260. X     */
  1261. X    while (n-- > 0) {
  1262. X        if (ch == '\f' || ch == '\n')
  1263. X            break;
  1264. X        *cs++ = ch;
  1265. X        if ((ch = getc(iop)) == EOF)
  1266. X            break;
  1267. X    }
  1268. X
  1269. X    if (ch == EOF && cs == s)        /* Nothing was read */
  1270. X        return((char *) NULL);
  1271. X    if (ch == '\f') {
  1272. X        if (ungetc(ch, iop) == EOF)
  1273. X            (void) fprintf(stderr, "fgetline - can't ungetc??\n");
  1274. X    }
  1275. X    else if (ch != '\n' && ch != EOF) {
  1276. X        fatal("fgetline - input line too long");
  1277. X        /*NOTREACHED*/
  1278. X    }
  1279. X    *cs = '\0';
  1280. X    return(s);
  1281. X}
  1282. X
  1283. X/*VARARGS*/
  1284. fatal(s, a, b, c, d, e, f, g, h, i, j)
  1285. char *s;
  1286. X{
  1287. X
  1288. X    (void) fprintf(stderr, "%s: ", progname);
  1289. X    (void) fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j);
  1290. X    (void) fprintf(stderr, "\n");
  1291. X    cleanup();
  1292. X    /*NOTREACHED*/
  1293. X}
  1294. X
  1295. X/*
  1296. X * Clean up and exit after an error
  1297. X */
  1298. cleanup()
  1299. X{
  1300. X
  1301. X    exit(1);
  1302. X}
  1303. X
  1304. END_OF_FILE
  1305. if test 26651 -ne `wc -c <'lwf.c'`; then
  1306.     echo shar: \"'lwf.c'\" unpacked with wrong size!
  1307. fi
  1308. # end of 'lwf.c'
  1309. fi
  1310. if test -f 'lwf.man' -a "${1}" != "-c" ; then 
  1311.   echo shar: Will not clobber existing file \"'lwf.man'\"
  1312. else
  1313. echo shar: Extracting \"'lwf.man'\" \(6229 characters\)
  1314. sed "s/^X//" >'lwf.man' <<'END_OF_FILE'
  1315. X.TH LWF 1-LOCAL "21 February 1988"
  1316. X.UC
  1317. X.SH NAME
  1318. lwf \- ASCII to PostScript filter
  1319. X.SH SYNOPSIS
  1320. X.B lwf
  1321. X[-c#] [-d] [-i#] [-l] [-m] [-olist] [-p[str]]
  1322. X.br
  1323. X.ti +4
  1324. X[-P filename] [-r] [-s#] [-S] [-t#] [-v] [file ...]
  1325. X.SH DESCRIPTION
  1326. X.I Lwf
  1327. takes one or more ASCII text files as input and produces PostScript
  1328. instructions that may be sent (see \fBlpr(1)\fR) to a PostScript printer
  1329. X(e.g., an Apple LaserWriter) for printing.
  1330. If no files are given on the command line, the standard input is read.
  1331. The program correctly handles the form feed character and tabs and
  1332. understands backspacing; underscores followed by backspaces
  1333. may be used to underline.
  1334. Courier font is used.
  1335. The output conforms to the Adobe 2.0 file structuring conventions.
  1336. X.PP
  1337. Note that flag arguments apply to all of the files in the argument list.
  1338. XFor example, using ``-s8'' prints each of the files in 8 point type.
  1339. X.PP
  1340. Multiple copies of each page can be printed by immediately following the
  1341. X.B -c
  1342. flag with the number of copies to make.
  1343. The pages are not collated.
  1344. X.PP
  1345. Lines may be indented (shifted to the right) using the \fB-i\fR flag.
  1346. This flag is immediately followed by the distance in inches to shift
  1347. all text from the left edge of the paper instead of the default amount
  1348. X(about 1/3 of an inch); the resolution is approximately 1/72 of an inch.
  1349. X.PP
  1350. The
  1351. X.B -l
  1352. flag indicates that landscape format is to be used instead of the default
  1353. portrait format.
  1354. X.PP
  1355. Margins suitable for use with a three hole punch can be obtained using the
  1356. X.B -m
  1357. flag.
  1358. This flag may be used with either portrait or landscape mode.
  1359. In portrait mode the
  1360. X.B -m
  1361. flag and any indentation specified by a
  1362. X.B -i
  1363. flag are additive.
  1364. X.PP
  1365. The
  1366. X.B -o
  1367. flag is immediately followed by a range specification that indicates
  1368. which pages are to be printed.
  1369. A range specification is a comma-separated list of numbers and ranges.
  1370. A number N selects the Nth page;
  1371. a range N:M selects the Nth through Mth pages, inclusive;
  1372. an initial :N selects from the beginning up to and including the
  1373. Nth page; and a final N: selects from the Nth page to the end.
  1374. The default, ``:'', is to print all pages.
  1375. X.PP
  1376. The
  1377. X.B -p
  1378. flag indicates that
  1379. X.B pr(1)
  1380. is to be used to perform pagination and print page headers.
  1381. An argument string to be passed on to \fBpr(1)\fR can immediately
  1382. follow the flag.
  1383. The usual way of producing multicolumn output is to pass a
  1384. X.B -n
  1385. flag to
  1386. X\fBpr(1)\fR, where
  1387. X.B n
  1388. is the number of columns to generate.
  1389. Note that this string must be properly quoted if it contains whitespace,
  1390. metacharacters, backslashes, etc.
  1391. X\fILwf\fR passes on the number of lines and columns to
  1392. X\fBpr(1)\fR so that multicolumn output is handled correctly.
  1393. When specifying multicolumns you are responsible for selecting
  1394. an appropriate point size and/or landscape format.
  1395. The formfeed character is handled incorrectly by some versions of
  1396. X\fBpr(1)\fR when multiple columns have been specified.
  1397. Instead of starting a new column or page it simply passes formfeeds
  1398. through.
  1399. X.B Lwf
  1400. filters out these formfeeds.
  1401. X.PP
  1402. X.I Lwf
  1403. normally includes a standard PostScript prologue in its output
  1404. X(usually /usr/local/lib/lwf.prologue).
  1405. The argument following a
  1406. X.B -P
  1407. flag specifies a prologue file to be substituted for the standard
  1408. system file.
  1409. X.PP
  1410. The default is to not perform page reversal, which is correct for printers
  1411. like the NEC Silentwriter LC-890.
  1412. The
  1413. X.B -r
  1414. flag enables page reversal so that the pages appear in
  1415. the correct sequence in the output tray of printers like the Apple
  1416. LaserWriter.
  1417. X.PP
  1418. The
  1419. X.B -s
  1420. flag, immediately followed by a 7, 8, 9, 10, 11, or 12 causes
  1421. the corresponding point size to be used.
  1422. The default point size is 10.
  1423. X.PP
  1424. By default, the output of
  1425. X.I lwf
  1426. is intended to go through a spooler that monitors the status of the
  1427. printer and separates jobs.
  1428. The
  1429. X.B -S
  1430. flag indicates that such a monitor is not being used and that a
  1431. header page should be printed to separate jobs and end-of-file characters
  1432. should be inserted in the output.
  1433. X.PP
  1434. The
  1435. X.B -t
  1436. flag is immediately followed by a number indicating the distance between
  1437. tab stops.
  1438. The default value is 8 characters.
  1439. X.PP
  1440. The
  1441. X.B -d
  1442. and
  1443. X.B -v
  1444. flags are used for debugging.
  1445. X.SH EXAMPLES
  1446. The command
  1447. X.sp 2
  1448. X.ti +5
  1449. lwf -p-2 -i0.5 -s12 file1.c file2.c | lpr -Plw
  1450. X.sp 2
  1451. would print the two files on printer 'lw' in portrait format with
  1452. page headings, indenting 0.5 inches from the left edge, using 12
  1453. point type.
  1454. Each file would be printed in two columns.
  1455. X.sp 1
  1456. The command
  1457. X.sp 2
  1458. X.ti +5
  1459. lwf -l -s7 -p'-h foo' | lpr -Plw
  1460. X.sp 2
  1461. would print the standard input with page headings in landscape format
  1462. using 7 point type.
  1463. The filename for the header line would be 'foo'.
  1464. X.sp 1
  1465. The command
  1466. X.sp 2
  1467. X.ti +5
  1468. lwf -p'-h foo .login -h bar .cshrc' | lpr -Plw
  1469. X.sp 2
  1470. would print the file ``.login'' with a header title ``foo'' and ``.cshrc''
  1471. with the header title ``bar''.
  1472. X.sp 1
  1473. A useful csh alias is:
  1474. X.sp 2
  1475. X.ti +5
  1476. alias prlw 'lwf -s7 -t4 -l -p-2 \\!* | lpr -Plw'
  1477. X.sp 2
  1478. which, when invoked as
  1479. X.sp 2
  1480. X.ti +5
  1481. prlw foo1 foo2
  1482. X.sp 2
  1483. prints foo1 and foo2, two columns per page, on printer 'lw'.
  1484. X.SH FILES
  1485. X/tmp/lwfXXXXXX        \- temporary file used for page reversal
  1486. X.SH SEE ALSO
  1487. pr(1), lpr(1)
  1488. X.SH LIMITATIONS
  1489. The maximum input line length is 1024 characters.
  1490. This should not present a problem since the corresponding output line
  1491. length would be too long to be printed.
  1492. The program quits and prints a message if an input line is too long.
  1493. Output lines that are too long are normally silently truncated by the
  1494. printer.
  1495. X.sp 2
  1496. X.I Lwf
  1497. can be compiled such that there is a limit on the number of pages it can
  1498. produce.
  1499. X.SH BUGS
  1500. It might be argued that flags should be allowed to be interspersed between
  1501. file arguments.
  1502. X.sp 2
  1503. The character '\\001' (SOH) should not appear in the input as it is used
  1504. within the program to delimit columns.
  1505. The first time the program reads this character it determines the number
  1506. of columns being printed; all subsequent lines are expected to
  1507. have this number of columns or be ``single column''.
  1508. X.sp 2
  1509. A \fB-s\fR flag should not be passed on to \fBpr(1)\fR since
  1510. X.I lwf
  1511. uses it.
  1512. X.sp 2
  1513. The program can only handle the 6 different point sizes and the single
  1514. font.
  1515. X.sp 2
  1516. So much for metric.
  1517. X.SH AUTHOR
  1518. Barry Brachman
  1519. X.br
  1520. Dept. of Computer Science
  1521. X.br
  1522. University of British Columbia
  1523. X
  1524. END_OF_FILE
  1525. if test 6229 -ne `wc -c <'lwf.man'`; then
  1526.     echo shar: \"'lwf.man'\" unpacked with wrong size!
  1527. fi
  1528. # end of 'lwf.man'
  1529. fi
  1530. if test -f 'prologue' -a "${1}" != "-c" ; then 
  1531.   echo shar: Will not clobber existing file \"'prologue'\"
  1532. else
  1533. echo shar: Extracting \"'prologue'\" \(565 characters\)
  1534. sed "s/^X//" >'prologue' <<'END_OF_FILE'
  1535. X%%EndComments
  1536. X% PostScript Prologue for lwf V2.0 ASCII to PostScript filter
  1537. X% Barry Brachman
  1538. X% Dept. of Computer Science
  1539. X% University of British Columbia
  1540. X/B {NW 0 rmoveto}bind def
  1541. X/EP {SV restore /#copies exch def showpage}bind def
  1542. X/L /lineto load def
  1543. X/M /moveto load def
  1544. X/NP /newpath load def
  1545. X/S /show load def
  1546. X/SHP {SP 2 setlinewidth}bind def
  1547. X/SLP {SP 612 0 translate 90 rotate}bind def
  1548. X/SP {/SV save def findfont exch scalefont setfont ( )
  1549. X  stringwidth pop dup /W exch def neg /NW exch def}bind def
  1550. X/ST /stroke load def
  1551. X/T {W mul 0 rmoveto}bind def
  1552. X%%EndProlog
  1553. END_OF_FILE
  1554. if test 565 -ne `wc -c <'prologue'`; then
  1555.     echo shar: \"'prologue'\" unpacked with wrong size!
  1556. fi
  1557. # end of 'prologue'
  1558. fi
  1559. if test -f 'range.c' -a "${1}" != "-c" ; then 
  1560.   echo shar: Will not clobber existing file \"'range.c'\"
  1561. else
  1562. echo shar: Extracting \"'range.c'\" \(7832 characters\)
  1563. sed "s/^X//" >'range.c' <<'END_OF_FILE'
  1564. X/* vi: set tabstop=4 : */
  1565. X
  1566. X/*
  1567. X * Return 1 if the given number is in the specified range,
  1568. X * -1 if there is an error in the range specification,
  1569. X * 0 otherwise
  1570. X *
  1571. X * Ranges have a similar (we use a colon instead of a dash) form to that
  1572. X * used by [nt]roff; i.e., a comma separated list of specifiers of the
  1573. X * form:
  1574. X *    1) n    means     x such that x = n
  1575. X *    2) :n    means all x such that x <= n
  1576. X *    3) n:    means all x such that x >= n
  1577. X *    4) n:m    means all x such that n <= x <= m
  1578. X *    5) :    means all x
  1579. X * n is an int
  1580. X *
  1581. X * Problems:
  1582. X * The routine prints an error message if the range is strange - this
  1583. X * might not always be desirable.
  1584. X *
  1585. X * Jul/86 BJB
  1586. X */
  1587. X
  1588. X/*
  1589. X * ===================================================================
  1590. X *
  1591. X * Permission is given to freely copy and distribute this software
  1592. X * providing:
  1593. X *
  1594. X *    1) You do not sell it,
  1595. X *    2) You do not use it for commercial advantage, and
  1596. X *    3) This notice accompanies the distribution
  1597. X *
  1598. X * Copyright (c) 1988
  1599. X * Barry Brachman
  1600. X * Dept. of Computer Science
  1601. X * Univ. of British Columbia
  1602. X * Vancouver, B.C. V6T 1W5
  1603. X *
  1604. X * .. {ihnp4!alberta, uw-beaver, uunet}!ubc-vision!ubc-csgrads!brachman
  1605. X * brachman@grads.cs.ubc.cdn
  1606. X * brachman%ubc.csnet@csnet-relay.arpa
  1607. X * brachman@ubc.csnet
  1608. X * ====================================================================
  1609. X */
  1610. X
  1611. X#include <ctype.h>
  1612. X#include <stdio.h>
  1613. X
  1614. X#define streq(a, b)            (!strcmp((a), (b)))
  1615. X#define smalloc(a, t, s)    ((a = (t) malloc((unsigned) (s))) == NULL)
  1616. X#define srealloc(a, t, b, s) ((a = (t) realloc(b, (unsigned) (s))) == NULL)
  1617. X#define max(a, b)            ((a) >= (b) ? (a) : (b))
  1618. X
  1619. X#define SEP_CHAR    ':'
  1620. X#define SEP_T        0        /* separator token */
  1621. X#define NUM_T        1        /* number token */
  1622. X#define BAD_T        2        /* token for bad character */
  1623. X
  1624. X#define STR_ALLOC    80
  1625. X
  1626. struct range_header {
  1627. X    char *range_str;        /* range character string */
  1628. X    int range_str_alloc;    /* length in bytes */
  1629. X    int nranges;            /* number of range entries */
  1630. X    struct range *range;
  1631. X} range_header = {
  1632. X    NULL, 0, NULL
  1633. X};
  1634. X
  1635. X/*
  1636. X * If hflag (lflag) is non-zero then the high (low) value is present
  1637. X */
  1638. struct range {
  1639. X    char hflag;            /* high value present */
  1640. X    char lflag;            /* low value present */
  1641. X    int high;            /* high part of range */
  1642. X    int low;            /* low part of range */
  1643. X};
  1644. X
  1645. X#ifdef RANGE_DEBUG
  1646. X
  1647. X/*
  1648. X * This is a program for demonstrating and debugging the range checking
  1649. X * code
  1650. X * Enter a range when prompted
  1651. X * (If there is a previous range shown you may enter <return> to
  1652. X * reselect it)
  1653. X * Enter a value
  1654. X * The program will indicate whether the value is in the given range
  1655. X */
  1656. X
  1657. char buf[BUFSIZ], range[BUFSIZ];
  1658. X
  1659. main(argc, argv)
  1660. int argc;
  1661. char **argv;
  1662. X{
  1663. X    register int i;
  1664. X    char *p;
  1665. X    struct range_header *rh;
  1666. X    struct range *r;
  1667. X    FILE *fp;
  1668. X    char *gets(), *index(), *strcpy();
  1669. X
  1670. X    buf[0] = range[0] = '\0';
  1671. X    if (argc == 2) {
  1672. X        if ((fp = fopen(argv[1], "r")) == NULL) {
  1673. X            (void) fprintf(stderr, "Can't open %s\n", argv[1]);
  1674. X            exit(1);
  1675. X            /*NOTREACHED*/
  1676. X        }
  1677. X    }
  1678. X    else
  1679. X        fp = stdin;
  1680. X
  1681. X    if (fp == stdin)
  1682. X        (void) printf("Range? ");
  1683. X    while (fgets(buf, sizeof(buf), fp) != NULL) {
  1684. X        if ((p = index(buf, '\n')) != NULL)
  1685. X            *p = '\0';
  1686. X        if (buf[0] != '\0') {
  1687. X            (void) strcpy(range, buf);
  1688. X            if (checkrange(range)) {
  1689. X                if (fp == stdin)
  1690. X                    (void) printf("Range? ");
  1691. X                continue;
  1692. X            }
  1693. X            rh = &range_header;
  1694. X            (void) printf("%s (%d alloc) (%d ranges):\n",
  1695. X                  rh->range_str, rh->range_str_alloc, rh->nranges);
  1696. X            for (r = rh->range, i = 0; i < rh->nranges; i++, r++)
  1697. X                (void) printf("hflag=%d lflag=%d high=%d low=%d\n",
  1698. X                    r->hflag, r->lflag, r->high, r->low);
  1699. X        }
  1700. X        if (fp != stdin)
  1701. X            continue;
  1702. X        (void) printf("Value? ");
  1703. X        if (gets(buf) == NULL)
  1704. X            break;
  1705. X        i = inrange(atoi(buf), range);
  1706. X        if (i == 0)
  1707. X            (void) printf("\tno\n");
  1708. X        else if (i == 1)
  1709. X            (void) printf("\tyes\n");
  1710. X        else if (i == -1)
  1711. X            (void) printf("\terror\n");
  1712. X        else
  1713. X            (void) printf("\tbad result\n");
  1714. X        (void) printf("Range ['%s']? ", range);
  1715. X    }
  1716. X    (void) printf("\n");
  1717. X}
  1718. X#endif RANGE_DEBUG
  1719. X
  1720. X/*
  1721. X * Check and compile the given range specification and then determine if
  1722. X * the number is in the range
  1723. X * Return -1 if there is a compilation error, 1 if the number is in the
  1724. X * range, or 0 if the number isn't in the range
  1725. X */
  1726. inrange(num, range_spec)
  1727. int num;
  1728. char *range_spec;
  1729. X{
  1730. X    register int i, rc;
  1731. X    register struct range_header *rh;
  1732. X    register struct range *r;
  1733. X
  1734. X    if (checkrange(range_spec))
  1735. X        return(-1);
  1736. X    rh = &range_header;
  1737. X    rc = 0;
  1738. X    for (r = rh->range, i = 0; rc == 0 && i < rh->nranges; i++, r++) {
  1739. X        if (r->hflag) {
  1740. X            if (num > r->high)
  1741. X                continue;
  1742. X            if (r->lflag && num < r->low)
  1743. X                continue;
  1744. X            rc = 1;
  1745. X        }
  1746. X        else if (r->lflag) {
  1747. X            if (num >= r->low)
  1748. X                rc = 1;
  1749. X        }
  1750. X        else                /* both unset -> ":" */
  1751. X            rc = 1;
  1752. X    }
  1753. X    return(rc);
  1754. X}
  1755. X
  1756. X/*
  1757. X * Check and compile a range specification
  1758. X * Print a message and return -1 on error; return 0 oth.
  1759. X *
  1760. X * Could be more efficient by allocating more structures at a time... SMOP
  1761. X */
  1762. checkrange(range_spec)
  1763. char *range_spec;
  1764. X{
  1765. X    register struct range_header *rh;
  1766. X    register struct range *r;
  1767. X    int len;
  1768. X    int ltype, lval, rtype, rval;
  1769. X    char *p;
  1770. X    char *malloc(), *realloc(), *strcpy();
  1771. X
  1772. X    rh = &range_header;
  1773. X    /*
  1774. X     * Check if the previous range is being used
  1775. X     */
  1776. X    if (rh->range_str != NULL && streq(range_spec, rh->range_str))
  1777. X        return(0);
  1778. X
  1779. X    /*
  1780. X     * New range spec
  1781. X     * If there is enough space, reuse it; oth. allocate enough
  1782. X     * (amount allocated never shrinks)
  1783. X     */
  1784. X    len = max(strlen(range_spec) + 1, STR_ALLOC);
  1785. X    if (rh->range_str != NULL  && len > rh->range_str_alloc) {
  1786. X        free(rh->range_str);
  1787. X        rh->range_str = (char *) malloc((unsigned) len);
  1788. X        rh->range_str_alloc = len;
  1789. X    }
  1790. X    else if (rh->range_str == NULL) {
  1791. X        rh->range_str = (char *) malloc((unsigned) len);
  1792. X        rh->range_str_alloc = len;
  1793. X    }
  1794. X    (void) strcpy(rh->range_str, range_spec);
  1795. X    if (rh->range != NULL)
  1796. X        free((char *) rh->range);
  1797. X    rh->range = NULL;
  1798. X
  1799. X    p = range_spec;
  1800. X    while (1) {
  1801. X        lval = getnum(&p, <ype);
  1802. X        if (ltype == BAD_T) {
  1803. X            (void) fprintf(stderr, "range: bad first number\n");
  1804. X            *rh->range_str = '\0';        /* invalidate */
  1805. X            return(-1);
  1806. X        }
  1807. X
  1808. X        if (rh->range == NULL) {
  1809. X            smalloc(r, struct range *, sizeof(struct range));
  1810. X            rh->nranges = 1;
  1811. X        }
  1812. X        else {
  1813. X            len = sizeof(struct range) * ++(rh->nranges);
  1814. X            srealloc(r, struct range *, (char *) rh->range, len);
  1815. X        }
  1816. X        rh->range = r;
  1817. X        r += rh->nranges - 1;        /* point to new one */
  1818. X        r->hflag = r->lflag = 0;
  1819. X        r->high = r->low = 0;
  1820. X
  1821. X        /*
  1822. X         * If ltype != NUM_T there is no lval
  1823. X         */
  1824. X        if (ltype == NUM_T) {
  1825. X            r->lflag = 1;
  1826. X            r->low = lval;
  1827. X        }
  1828. X
  1829. X        switch (*p) {
  1830. X        case ',':            /* single number */
  1831. X            r->hflag = 1;
  1832. X            r->high = lval;
  1833. X            p++;
  1834. X            continue;
  1835. X
  1836. X        case '\0':            /* single number at end */
  1837. X            r->hflag = 1;
  1838. X            r->high = lval;
  1839. X            return(0);
  1840. X
  1841. X        case ':':
  1842. X            p++;
  1843. X            if (*p == '\0')        /* no rval */
  1844. X                return(0);
  1845. X            if (*p == ',') {    /* no rval */
  1846. X                p++;
  1847. X                break;
  1848. X            }
  1849. X
  1850. X            rval = getnum(&p, &rtype);
  1851. X            if (rtype == BAD_T) {
  1852. X                (void) fprintf(stderr, "range: bad second number\n");
  1853. X                *rh->range_str = '\0';
  1854. X                return(-1);
  1855. X            }
  1856. X
  1857. X            if (lval > rval) {
  1858. X                (void) fprintf(stderr, "range: values reversed\n");
  1859. X                *rh->range_str = '\0';    /* invalidate */
  1860. X                return(-1);
  1861. X            }
  1862. X            r->hflag = 1;
  1863. X            r->high = rval;
  1864. X            if (*p == '\0')
  1865. X                return(0);
  1866. X            if (*p == ',')
  1867. X                p++;
  1868. X            break;
  1869. X
  1870. X        default:
  1871. X            (void) fprintf(stderr, "range: bad character\n");
  1872. X            *rh->range_str = '\0';        /* invalidate */
  1873. X            return(-1);
  1874. X        }
  1875. X    }
  1876. X}
  1877. X
  1878. static
  1879. getnum(pp, type)
  1880. char **pp;
  1881. int *type;
  1882. X{
  1883. X    register int sign, val;
  1884. X    register char *p;
  1885. X
  1886. X    p = *pp;
  1887. X    if (!isdigit(*p) && *p != '-') {
  1888. X        if (*p == SEP_CHAR)
  1889. X            *type = SEP_T;
  1890. X        else
  1891. X            *type = BAD_T;
  1892. X        return(0);
  1893. X    }
  1894. X    sign = 1;
  1895. X    if (*p == '-') {
  1896. X        sign = -1;
  1897. X        p++;
  1898. X    }
  1899. X    if (!isdigit(*p)) {
  1900. X        *type = BAD_T;
  1901. X        return(0);
  1902. X    }
  1903. X    for (val = 0; isdigit(*p) && *p != '\0'; p++)
  1904. X        val = val * 10 + *p - '0';
  1905. X    if (*p != '\0' && *p != ',' && *p != SEP_CHAR) {
  1906. X        *type = BAD_T;
  1907. X        return(0);
  1908. X    }
  1909. X    *pp = p;
  1910. X    *type = NUM_T;
  1911. X    return(sign * val);
  1912. X}
  1913. X
  1914. END_OF_FILE
  1915. if test 7832 -ne `wc -c <'range.c'`; then
  1916.     echo shar: \"'range.c'\" unpacked with wrong size!
  1917. fi
  1918. # end of 'range.c'
  1919. fi
  1920. echo shar: End of shell archive.
  1921. exit 0
  1922.